<?php
/**
 * Created by PhpStorm.
 * User: lengbin
 * Date: 2019-02-22
 * Time: 11:11
 */

namespace common\core\controller;

use yii\console\Exception;
use yii\helpers\FileHelper;
use yii\helpers\Console;

class MigrateController extends \yii\console\controllers\MigrateController
{

    protected $con;

    protected $dsnType;

    protected $host;

    protected $dbname;

    protected $port;

    /**
     * 获得 mysql 连接
     *
     * @param string $db
     *
     * @return \mysqli|null
     */
    protected function getConnect($db = '')
    {
        if ($this->con === null) {
            $this->con = new \mysqli($this->host, $this->db->username, $this->db->password, $db, $this->port);
        }
        return $this->con;
    }

    /*
     * 解析 dsn
     */
    protected function parseDsn()
    {
        if (empty($this->db->dsn)) {
            return;
        }
        list($type, $data) = explode(':', $this->db->dsn);
        $this->dsnType = $type;
        $params = explode(';', $data);
        foreach ($params as $param) {
            list($key, $value) = explode('=', $param);
            $this->$key = $value;
        }
    }

    /**
     * 是否数据库存在
     *
     * @return bool
     */
    protected function hasDb()
    {
        $status = 1;
        $this->parseDsn();
        // 目前 只支持 mysql
        if ($this->dsnType !== 'mysql') {
            return $status;
        }
        try {
            $this->getConnect($this->dbname);
        } catch (\Throwable $exception) {
            $status = 0;
            // 防止mysql连接报错
            try {
                $this->getConnect();
            } catch (\Throwable $exception) {
                $status = -1;
            }
        }
        return $status;
    }

    /**
     * 创建 db
     */
    protected function createDb()
    {
        $sql = "create database {$this->dbname} DEFAULT CHARACTER SET {$this->db->charset} COLLATE {$this->db->charset}_unicode_ci;";
        $this->con->query($sql);
    }

    /**
     * Upgrades the application by applying new migrations.
     *
     * 重写 up 方法
     * 每次执行先确认数据库是否存在 不存在则创建数据库
     *
     * @param int $limit
     *
     * @throws Exception
     */
    public function actionUp($limit = 0)
    {
        $status = $this->hasDb();

        if ($status === 0) {
            if ($this->confirm("Create a database  '" . $this->dbname . "'?")) {
                $this->createDb();
                $this->stdout("Database created successfully.\n", Console::FG_GREEN);
            }
            $status = 1;
        }

        if ($status < 0) {
            if ($this->confirm('PHP not support mysqli, please manually creating the database"' . $this->dbname . '", You are sure you have created the database？')) {
                $status = 1;
            }
        }

        if ($status > 0) {
            parent::actionUp($limit);
        }
    }

    /**
     * Generates class base name and namespace from migration name from user input.
     *
     * @param string $name migration name from user input.
     *
     * @return array list of 2 elements: 'namespace' and 'class base name'
     * @since 2.0.10
     */
    private function generateClassName($name)
    {
        $namespace = null;
        $name = trim($name, '\\');
        if (strpos($name, '\\') !== false) {
            $namespace = substr($name, 0, strrpos($name, '\\'));
            $name = substr($name, strrpos($name, '\\') + 1);
        } else {
            if ($this->migrationPath === null) {
                $migrationNamespaces = $this->migrationNamespaces;
                $namespace = array_shift($migrationNamespaces);
            }
        }

        if ($namespace === null) {
            $class = 'm' . gmdate('ymd_His') . '_' . $name;
        } else {
            $class = 'M' . gmdate('ymdHis') . ucfirst($name);
        }

        return [$namespace, $class];
    }

    /**
     * Finds the file path for the specified migration namespace.
     *
     * @param string|null $namespace migration namespace.
     *
     * @return string migration file path.
     * @throws Exception on failure.
     * @since 2.0.10
     */
    private function findMigrationPath($namespace)
    {
        if (empty($namespace)) {
            return is_array($this->migrationPath) ? reset($this->migrationPath) : $this->migrationPath;
        }

        if (!in_array($namespace, $this->migrationNamespaces, true)) {
            throw new Exception("Namespace '{$namespace}' not found in `migrationNamespaces`");
        }

        return $this->getNamespacePath($namespace);
    }

    /**
     * Returns the file path matching the give namespace.
     *
     * @param string $namespace namespace.
     *
     * @return string file path.
     * @since 2.0.10
     */
    private function getNamespacePath($namespace)
    {
        return str_replace('/', DIRECTORY_SEPARATOR, \Yii::getAlias('@' . str_replace('\\', '/', $namespace)));
    }

    /**
     * Migrate scripts are generated using SQL
     *
     * @param string $name
     *
     * @throws Exception
     * @throws \yii\base\Exception
     */
    public function actionSqlCreate($name = '')
    {
        $db = $this->db;
        $db->schemaMap = [
            'mysql' => 'common\core\mysql\Schema',
        ];
        $name = $name ? $name : 'sql_create';
        if (!preg_match('/^[\w\\\\]+$/', $name)) {
            throw new Exception('The migration name should contain letters, digits, underscore and/or backslash characters only.');
        }

        list($namespace, $className) = $this->generateClassName($name);
        $nameLimit = $this->getMigrationNameLimit();
        if ($nameLimit !== null && strlen($className) > $nameLimit) {
            throw new Exception('The migration name is too long.');
        }
        $migrationPath = $this->findMigrationPath($namespace);
        $file = $migrationPath . DIRECTORY_SEPARATOR . $className . '.php';
        if ($this->confirm("Create new migration '$file'?")) {
            $content = $db->schema->createSqlScript([
                'className' => $className,
                'namespace' => $namespace,
            ]);
            FileHelper::createDirectory($migrationPath);
            file_put_contents($file, $content, LOCK_EX);
            $this->stdout("New migration created successfully.\n", Console::FG_GREEN);
        }
    }

    /**
     * Initialize the SQL data， table multiple partitions are separated by |
     *
     * @param string $tableName
     * @param string $name
     *
     * @throws Exception
     * @throws \yii\base\Exception
     */
    public function actionSqlInit($tableName = '', $name = '')
    {
        $data = [];
        $db = $this->db;
        $db->schemaMap = [
            'mysql' => 'common\core\mysql\Schema',
        ];
        $tables = explode('|', $tableName);
        if (array_filter($tables)) {
            foreach ($tables as $table) {
                $tname = $db->schema->getRawTableName($table);
                $t = $db->schema->getTableSchema($tname);
                if (!empty($t)) {
                    $data[] = $t;
                }
            }
        }

        $name = $name ? $name : 'sql_create';
        if (!preg_match('/^[\w\\\\]+$/', $name)) {
            throw new Exception('The migration name should contain letters, digits, underscore and/or backslash characters only.');
        }

        list($namespace, $className) = $this->generateClassName($name);
        $nameLimit = $this->getMigrationNameLimit();
        if ($nameLimit !== null && strlen($className) > $nameLimit) {
            throw new Exception('The migration name is too long.');
        }
        $migrationPath = $this->findMigrationPath($namespace);
        $file = $migrationPath . DIRECTORY_SEPARATOR . $className . '.php';

        if ($this->confirm("Create new migration '$file'?")) {
            $content = $db->schema->initSqlScript([
                'className' => $className,
                'namespace' => $namespace,
                'table'     => $data,
            ]);
            FileHelper::createDirectory($migrationPath);
            file_put_contents($file, $content, LOCK_EX);
            $this->stdout("New migration created successfully.\n", Console::FG_GREEN);
        }
    }

}